package com.google.code.syncgear.core;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanMap;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.code.syncgear.core.annotation.EntityField;
import com.google.code.syncgear.core.exception.DatastoreOperationException;
import com.google.code.syncgear.util.DateTimeUtil;

public abstract class Model implements Serializable {

	private static final long serialVersionUID = 7819172100128124843L;
	
	private Key key;

	private Date createDate;

	private Date updateDate;
	
	public Key getKey() {
		return key;
	}

	public Date getCreateDate() {
		return createDate;
	}

	public Date getUpdateDate() {
		return updateDate;
	}

	public void save() throws DatastoreOperationException {
		try {
			Date now = DateTimeUtil.getNow();
			Entity entity = toEntity();
			if(this.key == null)
				entity.setProperty("createDate", now);
			else
				entity.setProperty("createDate", this.createDate);
			
			entity.setProperty("updateDate", now);
			this.key = Environment.getDatastoreService().put(entity);
		} catch (Exception ex) {
			throw new DatastoreOperationException(ex.getCause());
		}
	}

	public void remove() throws DatastoreOperationException {
		if(this.key != null) {
			try {
				Environment.getDatastoreService().delete(key);
				this.key = null;
			} catch (Exception ex) {
				throw new DatastoreOperationException(ex.getCause());
			}
		}
	}

	public Entity toEntity() {
		Class<? extends Model> modelClass = getClass();
		Entity entity = new Entity(modelClass.getSimpleName());
		if(this.key != null)
			entity = new Entity(this.key);				
		BeanMap beanMap = new BeanMap(this);		
		for (Field field : modelClass.getDeclaredFields()) {
			if(field.isAnnotationPresent(EntityField.class)) {
	            String fieldName = field.getName();
	            entity.setProperty(fieldName, beanMap.get(fieldName));
			}
		}
		return entity;
	}

	public void wrap(Entity entity) {
		Map<String, Object> properties = entity.getProperties();
		Set<String> propertyNames = properties.keySet();
		this.key = entity.getKey();
		
		Class<? extends Model> modelClass = getClass();
		for(String propertieName : propertyNames) {
			 try {
				Field field = modelClass.getDeclaredField(propertieName);
				field.setAccessible(true);				
				field.set(this, properties.get(propertieName));
			} catch (Exception ex) {
				continue;
			}
		}
		this.createDate = (Date)entity.getProperty("createDate");
		this.updateDate = (Date)entity.getProperty("updateDate");
	}

}
